# This example is the same as the last one but it now has the proxy model (sort and filter) logic implemented.
# 1. The QDataWidgetMapper class allows us to map data from a certain row and column of our model into a widget in Qt.
# The widget can be anything from a combobox, lineEdit or even a custom widget made in the designer or written from scratch.
# This mapping then allows us to use these widgets to modify the data at the corresponding row and column. For example,
# a lineEdit can be used to modify the name of a Node, spinBox for light intensity etc.
# All of this is done while maintaining the sync property of model view programming.
# 2. The properties of each item are placed in something called 'property grids'. The property grids are then embedded into the
# final application. We have created the different property grids in using 'widgets' in the Qt Designer.
# In this design, we have four nodes; Node, Camera, Transform and Light so we need to create four widgets.
# Node - has two properties. Name and Type Info. Name should be 'editable'. Type Info should be 'read only'. We put them in
# lineEdits. Labels have minimumWidth of 75. Type Info lineEdit has readOnly property selected.
# Lights has four properties. Light Intensity, Near Range, Far Range (double spin boxes) and Cast Shadows(chkBox).
# Camera has two properties. Motion blur (chkBox) and Shake Intensity (double spin box)
# Transform has one property. Position (x, y and z)

import sys
from PySide import QtCore, QtGui
import icons_rc                 # Import the icons file that was made earlier
from loadUiType import *

class Node(object):
    def __init__(self, name, parent = None):
        self._name = name       # Name of the node
        self._children = []     # List of Children of the node
        self._parent = parent   # Parent of the node
        
        # If there is a parent to this node, then this node must add itself to the child list of the parent node
        if parent is not None:
            parent.addChild(self)
        
    # typeInfo method will return a string which will describe the type of this item. Since it is a node, it will return type as Node
    def typeInfo(self):
        return "Node"
    
    # Add a new child to the list of children
    def addChild(self, child):
        self._children.append(child)    # Append the new child to the list of children using append method.
        
    # Similar to the add child method above but puts the child at a given arbitrary location.
    def insertChild(self, position, child):
        # We check if the position is within the boundary of the list. If not, we return false.
        if position < 0 or position > len(self._children):
            return False
        # Else put the child at the given position using the insert method. Set the parent of the child to self and return true 
        self._children.insert(position, child)
        child._parent = self
        return True
    
    # Just like the insert child method, the remove child method is implemented as follows.
    def removeChild(self, position):
        # We check if the position is within the boundary of the list. If not, we return false.
        if position < 0 or position > len(self._children):
            return False
        # Else get the child at the given position using the pop method. Then et the parent of the child to None and return true 
        child = self._children.pop(position)
        child._parent = None
        return True    

        
    # Method to return the name of an object
    def name(self):
        return self._name
    
    # Method to set the node name as required by the setData method
    def setName(self, name):
        self._name = name
    
    # Method to get the child corresponding to an index (or row)
    def child(self, row):
        return self._children[row]
    
    # Method to get the number of children
    def childCount(self):
        return len(self._children)
    
    # Method to get the parent of this node
    def parent(self):
        return self._parent
    
    # Method to get the index number of this node relative to its parent
    def row(self):
        if self._parent is not None:
            return self._parent._children.index(self)
        
    # Method to display the data in the console
    def log(self, tabLevel = -1):
        output = ""
        tabLevel += 1
        
        for i in range(tabLevel):
            output += "\t"
        
        output += self._name + "\n"
        
        for child in self._children:
            output += child.log(tabLevel)
        
        tabLevel -= 1
        return output
    
    def __repr__(self):
        return self.log()
    
    
# Let's make some subclasses from the Node class and call them LightNode, TransformNode, CameraNode. All of them would need to 
# reimplement the typeInfo method. Initially we forced each class to have a parent. But now we changed it be without a parent.
# ** We also have modified these nodes to include their attributes as protected members.
# ** Also implemented are the getters and setters for those properties.  
class LightNode(Node):
    def __init__(self, name, parent = None):
        # Call the super class constructor
        super(LightNode, self).__init__(name, parent)
        
        self._lightIntensity = 1.0
        self._nearRange = 40.0
        self._farRange = 80.0
        self._castShadows = True
        
    # Reimplement the typeInfo method
    def typeInfo(self):
        return "LightNode"
    
    # Getters
    def lightIntensity(self):
        return self._lightIntensity
    
    def nearRange(self):
        return self._nearRange
    
    def farRange(self):
        return self._farRange
    
    def castShadows(self):
        return self._castShadows
    
    # Setters to set the protected members
    def setLightIntensity(self, intensity):
        self._lightIntensity = intensity
    
    def setNearRange(self, nearRange):
        self._nearRange = nearRange
    
    def setFarRange(self, farRange):
        self._farRange = farRange
    
    def setCastShadows(self, castShadows):
        self._castShadows = castShadows
    
class CameraNode(Node):
    def __init__(self, name, parent = None):
        # Call the super class constructor
        super(CameraNode, self).__init__(name, parent)
        
        self._motionBlur = True
        self._shakeIntensity = 50.0
        
    # Reimplement the typeInfo method
    def typeInfo(self):
        return "CameraNode"
    
    # Getters
    def motionBlur(self):
        return self._motionBlur
    
    def shakeIntensity(self):
        return self._shakeIntensity
    
    # Setters
    def setMotionBlur(self, motionBlur):
        self._motionBlur = motionBlur
    
    def setShakeIntensity(self, shakeIntensity):
        self._shakeIntensity = shakeIntensity

class TransformNode(Node):
    def __init__(self, name, parent = None):
        # Call the super class constructor
        super(TransformNode, self).__init__(name, parent)
        
        self._x = 0.0
        self._y = 0.0
        self._z = 0.0
        
    # Reimplement the typeInfo method
    def typeInfo(self):
        return "TransformNode"
    
    # Getters
    def x(self):
        return self._x
    
    def y(self):
        return self._y
    
    def z(self):
        return self._z
    
    # Setters
    def setX(self, x):
        self._x = x
    
    def setY(self, y):
        self._y = y
    
    def setZ(self, z):
        self._z = z



# Now we will implement the tree model. To implement the tree model, we have to subclass QAbstractItemModel and provide methods for it.
class SceneGraphModel(QtCore.QAbstractItemModel):
    
    # User roles. the first user role has the enum value of 32 (as in Qt::ItemDataRole documentation). The next user role
    # should be 33.
    sortRole = QtCore.Qt.UserRole
    filterRole = QtCore.Qt.UserRole + 1
    # Once we have the roles, we can set them on our proxy models as self._proxyModel.setSortRole(SceneGraphModel.sortRole).
    # See the lines indicated by **
    
    """ Inputs: Node, QObject """
    def __init__(self, root, parent = None):
        super(SceneGraphModel, self).__init__(parent)
        self._rootNode = root
    
    """ Inputs: QModelIndex """
    """ Outputs: int """
    """ Return the amount of children an item has """
    """ Note that we don't need the parent argument in the 'list' and the 'table' model. Only for 'tree' models. """
    """ We get the rootNode if the parent is not valid, otherwise we get the parent. Then we just return the childCount of the node """
    def rowCount(self, parent):
        if not parent.isValid():
            parentNode = self._rootNode
        else:
            parentNode = parent.internalPointer()
        
        return parentNode.childCount()  
    
    """ Inputs: QModelIndex """
    """ Outputs: int """
    """ Column count controls the number of columns in the tree view. Giving a column count of 2 makes two columns in the header of the tree view """
    """ But it does not display the other sections in tree view style, that's because the parent method of our model returns 0 on the second """
    """ parameter, which is actually the column of the createIndex method. This is telling that make column 0 (the first column) a hierarchical """ 
    """ view only. Now how should we use the second column. We can use the second column to display the type of the node. """ 
    """ Sounds like a good idea. For that we have to change the data and the headerData methods """
    def columnCount(self, parent):
        return 2
        
    # Data method to display something on the view. We will return both text and icons through this method.
    """ Inputs: QModelIndex, int """
    """ Outputs: QVariant, strings are cast to QString which is a QVariant """
    # Remember that the data method returns something that the view wants to display. All we will do here is call interalPointer to get our node,
    # then if it is displayrole, return the node name.
    # If the index is valid, we pass the test. Get the node. If it's displayrole, return the node name.
    # The index is a QModelIndex and it stores information about where an item is. Column corresponds to the header section.
    def data(self, index, role):
        if not index.isValid():
            return None
        
        node = index.internalPointer()
        typeInfo = node.typeInfo()
        
        # What to do when the view wants to display data, is done thorough DisplayRole.
        # Similarly, what to do in case when the view wants to edit data is done through the EditRole.
        
        if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole: # to retain value when item is double clicked for editing
            if index.column() == 0:     # if item lies in zeroth column then return the node name
                return node.name()
            if index.column() == 1:     # if item is in first column, return the node 'typeinfo'
                return node.typeInfo()  # to display the node typeInfo in the second column
            
            if typeInfo == "CameraNode":
                if index.column() == 2:
                    return node.motionBlur()
                if index.column() == 3:
                    return node.shakeIntensity()
        
            if typeInfo == "LightNode":
                if index.column() == 2:
                    return node.lightIntensity()
                if index.column() == 3:
                    return node.nearRange()
                if index.column() == 4:
                    return node.farRange()
                if index.column() == 5:
                    return node.castShadows()

            if typeInfo == "TransformNode":
                if index.column() == 2:
                    return node.x()
                if index.column() == 3:
                    return node.y()
                if index.column() == 4:
                    return node.z()
                       
        # Icons are returned by using a decoration role.
        if role == QtCore.Qt.DecorationRole:
            if index.column() == 0:     # if item lies in zeroth column then first get the 'typeinfo'. Depending on that, return the icon.
                typeInfo = node.typeInfo()
                if typeInfo == "LightNode":
                    # For returning an icon, we have to construct one using the QtGui.QIcon() method. The QIcon takes a QPixmap as an input.
                    # So we construct that as well. The QPixmap, takes a resource as input. To retrieve a resource at runtime, we type
                    # :/resource_name. Note that we do not want to work with full paths here.
                    return QtGui.QIcon(QtGui.QPixmap(":/Light.png"))
                if typeInfo == "CameraNode":
                    return QtGui.QIcon(QtGui.QPixmap(":/Camera.png"))
                if typeInfo == "TransformNode":
                    return QtGui.QIcon(QtGui.QPixmap(":/Transform.png"))
        
        # **** What to return in case of sort and filter roles. Returning node.typeInfo will cause the sort and filtering
        # to be based upon typeInfo attribute rather than what is returned in the display role (node.name) as default.
        if role == SceneGraphModel.sortRole:
            return node.typeInfo()
           
        if role == SceneGraphModel.filterRole:
            return node.typeInfo()
        # From now on, both sorting and filtering will be based upon node.typeInfo rather than node.name.
        # It will not care even if the node names are in alphabetic order. 
    
    # HeaderData method to display something on the view's header
    """ Inputs: int, Qt::Orientation, int """
    """ Outputs: QVariant, strings are cast to QString which is a QVariant """
    """ Let's just return the string "SceneGraph" to display on the header """
    def headerData(self, section, orientation, role):
        if role == QtCore.Qt.DisplayRole:
            if section == 0:
                return "SceneGraph"
            else:
                return "TypeInfo"   # To show TypeInfo in the header
    
    # Flags method to select the items, or enable items so that they are not greyed out etc. 
    """ Inputs: QModelIndex """
    """ Outputs: int (flag) """
    """ Flags method, as in previous examples, needs to return that the items are enabled, the items are selectable and the items are editable """
    """ The itemIsEditable flag requires that we implement a method called setData """
    def flags(self, index):
        return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable
    
    """ Inputs: QModelIndex, QVariant, int (flag) """
    """ The setData method receives an index (QModelIndex), a value (QVariant - A QVariant is a data holder for Qt applications) and a role """
    """ We want the role to default to EditRole """
    """ We will use the internalPointer to get our node and then set its name """
    def setData(self, index, value, role = QtCore.Qt.EditRole):
        if index.isValid():
            node = index.internalPointer()
            typeInfo = node.typeInfo()
            if role == QtCore.Qt.EditRole:
                # If the index column is 0 (i.e. the node name column), then change the name.
                # Also calling setData manually wouldn't update the views that are using this model. They wouldn't notice
                # that the data has changed. So we need to emit a signal, which we get by inheriting QAbstractItemModel.
                # The signal is called dataChanged. It takes two parameters, from and to. Since we only want to update a
                # single item, we will pass the same index to both of them.  
                if index.column() == 0:
                    node.setName(value)
            
                if typeInfo == "CameraNode":
                    if index.column() == 2:
                        node.setMotionBlur(value)
                    if index.column() == 3:
                        node.setShakeIntensity(value)
            
                if typeInfo == "LightNode":
                    if index.column() == 2:
                        node.setLightIntensity(value)
                    if index.column() == 3:
                        node.setNearRange(value)
                    if index.column() == 4:
                        node.setFarRange(value)
                    if index.column() == 5:
                        node.setCastShadows(value)
    
                if typeInfo == "TransformNode":
                    if index.column() == 2:
                        node.setX(value)
                    if index.column() == 3:
                        node.setY(value)
                    if index.column() == 4:
                        node.setZ(value)

                self.dataChanged.emit(index, index)
                return True
        return False
    
    
    # Tree models and Tree view require two more special methods
    """ Remember that the QModelIndex class has valuable information about where and item was with the row and columns methods that return ints"""
    """ In the previous examples, we used those methods index our lists. """
    """ The QModelIndex also have something called and index pointer. The indexPointer returns a custom node instance which we implemented above """
    # Our goal of from this method is to return the parent of the given index and the returned value must be a QModelIndex class itself
    """ Inputs: QModelIndex """
    """ Outputs: QModelIndex """
    """ Returns the parent of the node with the given QModelIndex """
    def parent(self, index):
        node = self.getNode(index) # Returns a node
        parentNode = node.parent()
        # If the returned node is the 'root' node then return an empty QModelIndex because roots don't have a parent
        if parentNode == self._rootNode:
            return QtCore.QModelIndex()
        # If the returned node is not the root node, then it must be a child node. Now is the time to create a QModelIndex class object that
        # we will wrap the parent node into. This is done by the factor method called 'createIndex'. We have this method because of inheriting
        # from the QAbstractItemModel. First arg is an integer that corresponds to the row that the parent node lies in, so we implement the
        # custom method row() that we created above. The second arg can be 0 for now. The third argument is an object that can be anything. It's
        # also the object that will be returned when you call internal pointer of the index that this method returns. We will pass our parent node
        # to this argument.
        # self.createIndex(int, int, object=0)
        return self.createIndex(parentNode.row(), 0, parentNode)

    """ Inputs: int, int, QModelIndex """
    """ Outputs: QModelIndex """
    """ Returns the QModelIndex that corresponds to the given row, column and parent node """
    # The row and column are integers that the view acquires from the rowCount and the columnCount methods. It also receives a parent which it
    # acquires from the method implemented above. The index method is responsible for returning a child at the given row and given column of the
    # given parent.   
    def index(self, row, column, parent):
        parentNode = self.getNode(parent)
        
        # Now we have the parent node. After that, we ask the parentNode to get the child by calling our own child method and passing the row 
        # that we want the child from. The row is an integer.
        childItem = parentNode.child(row)
        
        # But if for some wierd reason the child is None, we return an empty QModelIndex. If child exists we create an index and return it.
        if childItem:
            return self.createIndex(row, column, childItem)
        else:
            return QtCore.QModelIndex

    # Let's make a custom method, that is actually not overriding anything.
    # It will act like a helper. If we pass a QModelIndex with a valid index, we will get the internal pointer (i.e. the node,
    # if it exists) and return it. Otherwise we return the root node.  
    # First check if the parent is valid. Because in the 'parent' method above notice that we return an empty QModelIndex in case of a root
    # node, which is not valid. Therefore we set the parent node here to rootNode if the parent is not valid i.e. an empty QModelIndex. 
    # But if parent is valid then we get the internal pointer which again corresponds to our node instance.
    """ Custom method (not overriding anything) """
    """ Inputs: QModelIndex """
    def getNode(self, index):
        if index.isValid():
            node = index.internalPointer()
            if node:
                return node
        else:
            return self._rootNode
        
    # Methods for Inserting and Removing rows at runtime.
    # Recall that in the last examples, we ignored the parent arguments. Now is the time we will use it.
    # Insert rows acts as an insert children method, i.e. you are basically inserting children to this parent.
    # For that we will make an insert child method in the Node class which will add a child at an arbitrary location. 
    """ Inputs: int, int, QModelIndex """
    def insertRows(self, position, rows, parent = QtCore.QModelIndex()):
        # To insert the children, we need to get the parent node and we get it by calling our customly implemented getNode method
        # by passing the parent QModelIndex class to it. As a result it returns its internalPointer which is the name of the node.
        parentNode = self.getNode(parent)
    # Inputs are parent to insert at, the position to insert at, and how many items (more accurately, the ending index of items)
        self.beginInsertRows(parent, position, position + rows - 1)
        for row in range(rows):
            # Keep track of child count
            childCount = parentNode.childCount()
            # Create a node
            childNode = Node("NewNode" + str(childCount))
            # Insert this child node. The current child node pushes the previous child nodes, downwards 
            success = parentNode.insertChild(position, childNode)
        self.endInsertRows()
        return success
    
    """ Inputs: int, int, QModelIndex """
    def removeRows(self, position, rows, parent = QtCore.QModelIndex()):
        parentNode = self.getNode(parent)
        self.beginRemoveRows(parent, position, position + rows - 1)
        for row in range(rows):
            success = parentNode.removeChild(position)
        self.endRemoveRows()
        return success
    
    # As a demonstration we can also insert light nodes
    def insertLightRows(self, position, rows, parent = QtCore.QModelIndex()):
        parentNode = self.getNode(parent)
        self.beginInsertRows(parent, position, position + rows - 1)
        for row in range(rows):
            # Keep track of child count
            childCount = parentNode.childCount()
            # Create a node
            childNode = LightNode("NewLightNode" + str(childCount))
            # Insert this child node. The current child node pushes the previous child nodes, downwards 
            success = parentNode.insertChild(position, childNode)
        self.endInsertRows()
        return success
    
# The uic module contains a method called loadUiType (here I have used a custom user implementation of that module).
# We input the .ui file saved from the Qt Designer to this function. This method returns the form class and the base class,
# which we need to derive from, and create a new window.
# The propeties.ui file will contain a group box containing two horizontal layouts aligned vertically. The vertical layout 
# above will always contain node properties. The lower vertical layout will hold the specialized properties depending upon 
# user selection. The properties.ui will be located on the lower part of our window.
base, form = loadUiType("myUI.ui")
nodeBase, nodeForm = loadUiType("nodeProperties.ui")
lightBase, lightForm = loadUiType("lightProperties.ui")
cameraBase, cameraForm = loadUiType("cameraProperties.ui")
transformBase, transformForm = loadUiType("transformProperties.ui")
propBase, propForm = loadUiType("properties.ui")


# Let's create a new class that derives from the form and the base class and call that Wnd. It is derived from form and base class.
class Wnd(base, form):
    # In the constructor, we need to call the super class constructor of the base class and pass the parent to it. Then we also
    # need to call the setupUi method that gets generated by the loadUiType method. setupUi method actually initializes all our
    # controls placed in the Qt Designer. 
    def __init__(self, parent=None):
        super(base, self).__init__(parent)
        self.setupUi(self)
        
        rootNode = Node("Head")
        childNode0 = TransformNode("apple", rootNode)
        childNode1 = LightNode("app",     rootNode)
        childNode2 = CameraNode("cat",    rootNode)
        childNode3 = TransformNode("dog", rootNode)
        childNode4 = LightNode("elephant",     rootNode)
        childNode5 = CameraNode("fig",    rootNode)
        childNode6 = TransformNode("goat", rootNode)
        childNode7 = CameraNode("hat",    rootNode)
        childNode8 = LightNode("ink",     rootNode)
        childNode9 = LightNode("junk", childNode8)
        childNode10 = CameraNode("kite", childNode8)
        childNode11 = LightNode("light", childNode10)
        
        # Implement the filter
        # Our goal is to type something in the lineEdit and based on that, the uiTree should show the filtered results.
        # For that we have to use the QSortFilterProxyModel class.  So let's create one.        
        self._proxyModel = QtGui.QSortFilterProxyModel()
        # We put our proxy model in between. So instead of hooking the model directly into the view, we will hook the model
        # into the proxy model and then take the proxy model and hook it into the view. Also we made the models as protected
        # members using the self._model notations.
        self._model = SceneGraphModel(rootNode, self)   # <------ This is the data model
        self._proxyModel.setSourceModel(self._model) # <------ This is the proxy model
        self._proxyModel.setDynamicSortFilter(True) # To enable dynamic sorting as text is entered
        self._proxyModel.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) # To enable case insensitivity
        # ** Setting the user defined sort and filter roles
        # Then in the data method we need to return the appropriate data as the result of sort and filter roles. 
        self._proxyModel.setSortRole(SceneGraphModel.sortRole)
        self._proxyModel.setFilterRole(SceneGraphModel.filterRole)
        # setFilterKeyColumn for filtering a particular column. Column 0 in this case. However note that I must decouple
        # the user filter role from the proxy model (i.e. comment self._proxyModel.setFilterRole(SceneGraphModel.filterRole)
        self._proxyModel.setFilterKeyColumn(0) # *** don't forget to comment out setFilterRole otherwise this will be useless.
        self.uiTree.setModel(self._proxyModel)     # <------ This is the view
        # All we need to do now is to call the setFilterRegExp with the required string.
        # For filtering out data using hardcoded filter string we can use the following code
        # self._proxyModel.setFilterRegExp("Light")
        # But we want to use our lineEdit's (we named the lineEdit, uiFilter) current text. For that we need to use signals
        # and slots provided by the Qt framework.
        # LineEdit emits the signal textChanged when the text is changed by the user. The signal also holds a parameter with the
        # current text. So if we would connect that signal to the setFilterRegExp method, it will automatically set the new filter
        # text whenever we edit the filter field. To connect the textChanged signal to the setFilterRegExp method we use this code.
        # connect() is a static function. First parameter is the signal emitter, second is.. which signal we want to connect i.e.
        # here we have the textChanged signal with type QString. Third is whose the receiver of that signal. Here it is the proxy
        # model and the function from the proxy model is setFilterRegExp.
        QtCore.QObject.connect(self.uiFilter, QtCore.SIGNAL("textChanged(QString)"), self._proxyModel.setFilterRegExp)
        
        # Now let's implement sorting
        # For that we only need to call setSortingEnabled method on our uiTree.
        # Note: It would show an error if we already don't have a proxy model in between the data model and the view. 
        self.uiTree.setSortingEnabled(True)
        # If you need a different logic or process than QSortFilterProxyModel then you should consider subclassing the
        # QtGui.QAbstractProxyModel and implement the behavior you are after.

        # Now instantiate the propertiesEditor class inside our main window class.
        # Create an object of properties editor class with self as the parent.
        self._propEditor = PropertiesEditor(self)
        self.layoutMain.addWidget(self._propEditor)
        # Finally set the model of our propEditor with our proxyModel
        self._propEditor.setModel(self._proxyModel)
        
        # This is how we are connecting the currentChanged signal to our setSelection method.
        # Connecting the signal with our widget. The "owner" of the signal is the self.uiTree.selectionModel. Which signal,
        # we want to connect, the currentChanged signal. Who is gonna receive the parameters, the nodeEditor's setSelection function
        QtCore.QObject.connect(self.uiTree.selectionModel(), QtCore.SIGNAL("currentChanged(QModelIndex, QModelIndex)"), self._propEditor.setSelection)
        

# Once we have the form and base class uic modules, then we can create classes that are derived from the form class and the
# base class and then call the setypUi method to initialize our controllers. So we see that we are creating the classes by
# deriving from base and form classes. Then we call setupUi. We do that for all the Forms we made in designer.
 
""" NODE """
class NodeEditor(nodeBase, nodeForm):
    def __init__(self, parent=None):
        super(nodeBase, self).__init__(parent)
        self.setupUi(self)
        # Now, the QDataWidgetMapper has to be used on the classes. We need one WDataWidgetMapper object per class.
        self._dataMapper = QtGui.QDataWidgetMapper()
    # Then we need to set the model that the QDataWidgetMapper will use. That's done by calling setModel. Since our
    # dataMapper is a protected member, we should provide our own method for setting the model.
    # The model may be any model but we since we are setting a proxy model that is why we have named the model variable as
    # proxyModel variable.
    def setModel(self, proxyModel):
        # temporarily store the model then set it
        self._proxyModel = proxyModel
        # The data mapper does not want a proxy model since that is not where the implementation of how to receive the data
        # is. It wants our original data model. Fortunately, to give it that is easy. The proxy model calss has a getter called
        # source model. So on the dataMapper.setModel we will provide the sourceModel, since the data lies in the sourceModel.  
        self._dataMapper.setModel(proxyModel.sourceModel())
        # Once the model is set, we can addMapping. The addMapping method takes two arguments, a specific column and the
        # widget that you want to map to. The widget will correspond to the uiName and uiType here. The data will be retrieved
        # from the column and displayed in the widget. And, if you edit the data in the widget, it will be submitted back to
        # the same model, into the same column. The QDataWidgetMapper uses only the displayRole. If we inspect the data method
        # of our model, we see that for column 0, we return the name of the node and for column 1, we return the typeInfo.
        # Even if we may not want to display the typeInfo in a separate column (by using columnCount of 1, we could still
        # map our widget to the data inside of that column. 
        self._dataMapper.addMapping(self.uiName, 0) # name is in column 0 
        self._dataMapper.addMapping(self.uiTypeInfo, 1) # typeInfo is in column 1
        # The setModel method we just created, should be called in our window class with our data model.
        # For testing, let's select the first item in our model for editing. For that we can use the toFirst() method.
        # Now we can see the first item of our model in the lineEdit widgets, to be edited. However we usually don't do
        # that. We usually edit the item that we selected and not the first item only. 
        # self._dataMapper.toFirst()
    # Let's make a method to set the selected data in the tree view instead of the first column. This method should take two
    # arguments, current selection and old selection. But the nodeEditor.setSelection method does not need the oldSelection
    # parameter anymore because it will receive the selection from our propertiesEditor and not from a QtCore.SIGNAL, which
    # actually sends two QModelIndex-es
    # Remember that QModelIndex-es store valuable information about an item. 
    """ Inputs: QModelIndex """
    def setSelection(self, currentSelection):
        # Get the parent from the current QModelIndex (or the currentSelection)
        parent = currentSelection.parent()
        # Once we have the parent, we need to set it as our root index in our data mapper.
        self._dataMapper.setRootIndex(parent)
        # Finally we need to set the current QModelIndex to the current selection.
        self._dataMapper.setCurrentModelIndex(currentSelection)
        # All that is left now is now is using a signal called currentChange and connect it with our setSelection method. The
        # owner of this signal is the selection model of our tree view. We haven't talked about selection models yet. But you
        # should know that they exist and can be used "to sync the selection of two views".
        # For that, see the window class. The signal is connected in the bottom lines of the window class.
        # Now, whenever we select an item (be it root, parent or child), we can see and edit it using the lineEdit fields.
        # And it works on the children as well. That's because we have set the root of the QDataWidget mapper.
        # We have a working "property grid" which updates with our current selection and is also able to edit the data.

        
""" LIGHT """
class LightEditor(lightBase, lightForm):
    def __init__(self, parent=None):
        super(lightBase, self).__init__(parent)
        self.setupUi(self)
        
        self._dataMapper = QtGui.QDataWidgetMapper()

    def setModel(self, proxyModel):
        self._proxyModel = proxyModel
        self._dataMapper.setModel(proxyModel.sourceModel())
        
        self._dataMapper.addMapping(self.uiLightIntensity, 2) 
        self._dataMapper.addMapping(self.uiNear, 3)
        self._dataMapper.addMapping(self.uiFar, 4) 
        self._dataMapper.addMapping(self.uiShadows, 5)
        
    """ Inputs: QModelIndex """
    def setSelection(self, currentSelection):
        parent = currentSelection.parent()
        self._dataMapper.setRootIndex(parent)
        self._dataMapper.setCurrentModelIndex(currentSelection)
        
        
""" CAMERA """
class CameraEditor(cameraBase, cameraForm):
    def __init__(self, parent=None):
        super(cameraBase, self).__init__(parent)
        self.setupUi(self)
        
        self._dataMapper = QtGui.QDataWidgetMapper()

    def setModel(self, proxyModel):
        self._proxyModel = proxyModel
        self._dataMapper.setModel(proxyModel.sourceModel())
        
        self._dataMapper.addMapping(self.uiMotionBlur, 2) 
        self._dataMapper.addMapping(self.uiShakeIntensity, 3)
        
    """ Inputs: QModelIndex """
    def setSelection(self, currentSelection):
        parent = currentSelection.parent()
        self._dataMapper.setRootIndex(parent)
        self._dataMapper.setCurrentModelIndex(currentSelection)

        
""" TRANSFORM """
class TransformEditor(transformBase, transformForm):
    def __init__(self, parent=None):
        super(transformBase, self).__init__(parent)
        self.setupUi(self)
        
        self._dataMapper = QtGui.QDataWidgetMapper()
        
    def setModel(self, proxyModel):
        self._proxyModel = proxyModel
        self._dataMapper.setModel(proxyModel.sourceModel())
        
        self._dataMapper.addMapping(self.uiX, 2) 
        self._dataMapper.addMapping(self.uiY, 3)
        self._dataMapper.addMapping(self.uiZ, 4) 
        
    """ Inputs: QModelIndex """
    def setSelection(self, currentSelection):
        parent = currentSelection.parent()
        self._dataMapper.setRootIndex(parent)
        self._dataMapper.setCurrentModelIndex(currentSelection)
        
        
""" Properties Editor """
class PropertiesEditor(propBase, propForm):
    def __init__(self, parent=None):
        super(propBase, self).__init__(parent)
        self.setupUi(self)
        # A protected member to store the proxy model.
        self._proxyModel = None
        
        # Create the editors inside our propery editor class. Note that the editors take a parent argument in the constructor.
        # By putting the properties window as the parent, the editors will be in the properties window.
        self._nodeEditor = NodeEditor(self)
        self._lightEditor = LightEditor(self)
        self._cameraEditor = CameraEditor(self)
        self._transformEditor = TransformEditor(self)
        
        # We want to dock the editors into the vertical layout elements in the UI
        self.layoutNode.addWidget(self._nodeEditor)
        self.layoutSpecs.addWidget(self._lightEditor)
        self.layoutSpecs.addWidget(self._cameraEditor)
        self.layoutSpecs.addWidget(self._transformEditor)
        
        # Hide all the editors except the node editor as required.
        self._lightEditor.setVisible(False)
        self._cameraEditor.setVisible(False)
        self._transformEditor.setVisible(False)
        
    def setModel(self, proxyModel):
        # Store the proxyModel variable to a protected member
        self._proxyModel = proxyModel
        # Calling the setModel method to set the model to our proxyModel.
        self._nodeEditor.setModel(proxyModel) 
        self._lightEditor.setModel(proxyModel) 
        self._cameraEditor.setModel(proxyModel) 
        self._transformEditor.setModel(proxyModel) 

    # Let's make a setSelection method. But we will only delegate the currentSelection to our node class.
    def setSelection(self, currentSelection, oldSelection):
        # The currentSelection is an QModelIndex class object. It is received from the QProxy class which might have sorted
        # everything. However the values it holds might not match our original data model. So we need to map it bact to
        # the source. For that a function called mapToSource is available. It takes a QModelIndex and returns a mapped
        # QModelIndex. So we take the currentSelection which is a QModelIndex, map it to source and retrieve a new QModelIndex.
        currentSelection = self._proxyModel.mapToSource(currentSelection)
        
        node = currentSelection.internalPointer()
        if node is not None:
            typeInfo = node.typeInfo()
            # Depending upon typeInfo, we can show our editors
        if typeInfo == "CameraNode":
            self._lightEditor.setVisible(False)
            self._cameraEditor.setVisible(True)
            self._transformEditor.setVisible(False)
        elif typeInfo == "LightNode":
            self._lightEditor.setVisible(True)
            self._cameraEditor.setVisible(False)
            self._transformEditor.setVisible(False)
        elif typeInfo == "TransformNode":
            self._lightEditor.setVisible(False)
            self._cameraEditor.setVisible(False)
            self._transformEditor.setVisible(True)
        else:
            self._lightEditor.setVisible(False)
            self._cameraEditor.setVisible(False)
            self._transformEditor.setVisible(False)

        # The editors no longer need the old selection parameter because they will receive the selection from a properties
        # editor and not from a QtCore signal which actually sends two QModelIndex-es
        self._nodeEditor.setSelection(currentSelection)
        self._cameraEditor.setSelection(currentSelection)
        self._lightEditor.setSelection(currentSelection)
        self._transformEditor.setSelection(currentSelection)
        


if __name__ == "__main__":
    
    appObject = QtGui.QApplication(sys.argv)
    appObject.setStyle('cleanlooks')
        
    # Create the class and show it
    wnd = Wnd()
    wnd.show()
    
    
    # Execute the application
    sys.exit(appObject.exec_())
